package com.jellyfishumbrella;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Random;

import megamu.mesh.Region;

import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;

import com.jellyfishumbrella.GL.AABB;
import com.jellyfishumbrella.GL.AbstractGameLogic;
import com.jellyfishumbrella.GL.AbstractLOD;
import com.jellyfishumbrella.GL.AnimObject;
import com.jellyfishumbrella.GL.Animator;
import com.jellyfishumbrella.GL.MeshGL;
import com.jellyfishumbrella.GL.OctreeNode;
import com.jellyfishumbrella.GL.Polygon;
import com.jellyfishumbrella.GL.PolygonGroup;
import com.jellyfishumbrella.GL.Ray;
import com.jellyfishumbrella.GL.RayTraceResult;
import com.jellyfishumbrella.GL.Vector3d;

public class GameLogic extends AbstractGameLogic {
	public static double	SCALING					= 1;
	public int				landscapePoints			= 300;

	Random					R						= new Random();
	AABB					maxAABB;

	VorLand2D				vorland;
	MeshGL					vorlandOutline;

	PolygonGroup			prevGroupUnderCamera	= null;
	PolygonGroup			currGroupUnderCamera	= null;

	public GameLogic() {
		super();
	}

	public void init() {
		// this runs AFTER we're connected to Renderer

		preLoadTextures();

		animator = new Animator(this);
		animator.add(renderGL.camera);

		vorland = new VorLand2D(this);
	}

	/**
	 * Notification that a frame is being rendered. Responsible for
	 * running game logic and rendering the scene.
	 */
	public void gameLogic() {
		Display.sync(refreshRate);

		// work out how long its been since the last update, this
		// will be used to calculate how far the entities should
		// move this loop
		long delta = getTime() - lastLoopTime;
		lastLoopTime = getTime();
		lastFpsTime += delta;
		fps++;

		// update our FPS counter if a second has passed
		if (lastFpsTime >= 1000) {
			System.out.println("fps: " + fps + "   currTime: " + getTime() + "    Regions: " + vorland.getRegionCount());
			lastFpsTime = 0;
			fps = 0;
		}

		// if escape has been pressed, stop the game
		if (Display.isCloseRequested() || Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
			gameRunning = false;
			shutdown();
		}

		update(lastLoopTime);

		//updateLOD();
	}

	public void testOctree() {
		if (octree != null && octree.isComplete) {
			ArrayList<OctreeNode> test = octree.getIntersectedNodes(new Ray(renderGL.camera.pos(), new Vector3d(0, -1, 0)), 0, 999999);
			if (test != null) {
				for (OctreeNode currNode : test) {
					System.out.println(currNode.subDlevel + "   " + currNode.index_i + "   " + currNode.index_j + "   " + currNode.index_k);
				}
			}
		}
	}

	public synchronized void updateLOD() {
		if (octree != null && octree.isComplete) {
			//ArrayList<OctreeNode> octreeList = octree.getIntersectedNodes(new Ray(renderGL.camera.pos(), new Vector3d(0, -1, 0)), 0, 999999);
			ArrayList<OctreeNode> octreeList = octree.getIntersectedNodesY(new Ray(renderGL.camera.pos(), new Vector3d(0, -1, 0)));
			RayTraceResult underCamera = getStuffUnder(renderGL.camera, octreeList);
			prevGroupUnderCamera = currGroupUnderCamera;
			currGroupUnderCamera = underCamera.polyGroup;

			LinkedHashSet<AbstractLOD> updateList = new LinkedHashSet<AbstractLOD>();

			if (prevGroupUnderCamera != null) {
				//			updateLOD.addAll(prevGroupUnderCamera.preSetLOD(0));
			}
			if (currGroupUnderCamera != null && currGroupUnderCamera.LOD < currGroupUnderCamera.maxLOD) {
				updateList.addAll(currGroupUnderCamera.preSetLOD(currGroupUnderCamera.maxLOD));
			}

			Iterator<AbstractLOD> itr = updateList.iterator();
			while (itr.hasNext()) {
				Region currRegion = (Region) itr.next();
				currRegion.updateLOD();
				//System.out.println(currRegion.toString());
			}
		}
	}

	public RayTraceResult getStuffUnder(AnimObject animObj, ArrayList<OctreeNode> octreeList) {
		RayTraceResult under = new RayTraceResult();

		double altitude = animObj.keyframes.here.pos.y;
		if (octreeList != null) {
			synchronized (vorland.polyGroups) {
				for (OctreeNode<PolygonGroup> currOctree : octreeList) {
					//java.util.Iterator<PolygonGroup> itr = currOctree.polyGroups.iterator();
					//while (itr.hasNext()) {
					//	PolygonGroup currPolyGroup = itr.next();
					for (PolygonGroup currPolyGroup : currOctree) {
						java.util.Iterator<Polygon> itr2 = currPolyGroup.polygons.iterator();
						while (itr2.hasNext()) {
							Polygon currPoly = itr2.next();
							Vector3d here = animObj.keyframes.here.pos.clone();
							//Vector3d there = here.clone();
							//here.y += 100000;
							//there.y -= 100000;
							Vector3d intersection = currPoly.rayIntersection(new Ray(here, new Vector3d(0, -1, 0)), 0, 9999999);
							if (intersection != null) {
								under.mesh = currPoly.mesh;
								under.polyGroup = currPolyGroup;
								under.polygon = currPoly;
								under.point = intersection;
								return under;
							}
						}
					}
				}
			}
		}

		return under;
	}

	public double placeOnGround(AnimObject animObj, VorLand2D ground) {
		double headHeight = 4;
		double altitude = animObj.keyframes.here.pos.y;
		if (ground != null && ground.READY) {
			synchronized (ground.polyGroups) {
				java.util.Iterator<PolygonGroup> itr = ground.polyGroups.iterator();
				while (itr.hasNext()) {
					PolygonGroup currPolyGroup = itr.next();
					java.util.Iterator<Polygon> itr2 = currPolyGroup.polygons.iterator();
					while (itr2.hasNext()) {
						Polygon currPoly = itr2.next();
						Vector3d here = animObj.keyframes.here.pos.clone();
						Vector3d there = here.clone();
						here.y += 10000;
						there.y -= 10000;
						Vector3d intersection = currPoly.rayIntersection(new Ray(here, new Vector3d(0, -1, 0)), 0, 999999);
						if (intersection != null) {
							altitude = here.y - intersection.y;
							animObj.keyframes.here.pos.y = intersection.y + headHeight;
							break;
						}
					}
				}
			}
		}
		return altitude;
	}

	public void preLoadTextures() {
		renderGL.textureHash.loadTexture("testUV_grad");
		renderGL.textureHash.loadTexture("testFractal_Noise");
		renderGL.textureHash.loadTexture("test_ALT_GRAD");
	}

}